package com.ckw.match.server.impl;

import com.ckw.common.utils.SnowflakeIdWorker;
import com.ckw.match.filter.BloomFilterInitializer;
import com.ckw.match.lock.RedisDistributedLock;
import com.ckw.match.mapper.MatchMapper;
import com.ckw.match.pojo.Match;
import com.ckw.match.server.MatchService;
import com.ckw.match.task.DoScheduledController;
import com.ckw.match.utils.SerializationUtils;
import com.ckw.question.mapper.QuestionMapper;
import com.ckw.question.mapper.RecordMapper;
import com.ckw.question.pojo.MatchResult;
import com.ckw.question.pojo.Question;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class MatchServiceImpl implements MatchService {

    @Autowired
    private MatchMapper matchMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private RecordMapper recordMapper;

    @Autowired
    private DoScheduledController doScheduledController;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private RedisDistributedLock redisDistributedLock;

    @Autowired
    private BloomFilterInitializer bloomFilterInitializer;

    private static final long EXPIRE_TIME = 30; // 过期时间，单位：秒

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean addMatch(Match match) {
        match.setId(SnowflakeIdWorker.snowFlow.nextId());
        match.setState("未开始");
        match.setCreateTime(new SimpleDateFormat("yyyy-MM-dd- HH:mm").format(System.currentTimeMillis()));
        matchMapper.addMatch(match);
        addMatchQuestion(match.getQuestionIds(),match.getId());
//        刷新任务列表
        doScheduledController.startTask();
        bloomFilterInitializer.addMid(match.getId());
        return true;
    }

    @Override
    public List<Match> getMatchList() {
        return matchMapper.getMatchList();
    }

    @Override
    public boolean addMatchQuestion(List<String> qid,long mid) {
        return matchMapper.addMatchQuestion(qid,mid);
    }

    /**
     * @param mid
     * @return
     */
    @Override
    public boolean deleteMatchQuestion(String mid) {
        return matchMapper.deleteMatchQuestion(mid);
    }

    @Override
    @Transactional
    public Match getMatchDetail(long mid) {
//        拿到比赛题目的id
        List<String> matchQuestionIds = matchMapper.getMatchQuestionIds(mid);
//        根据得到id查题目内容
        List<Question> matchQuestion = new ArrayList<>();
        if(matchQuestionIds.size() != 0){
            matchQuestion = questionMapper.getMatchQuestion(matchQuestionIds);
        }
//        查询比赛
        Match matchDetail = matchMapper.getMatchDetail(mid);
        matchDetail.setQuestions(matchQuestion);
        matchDetail.setQuestionIds(matchQuestionIds);

        return matchDetail;
    }

    @Transactional
    @Override
    public boolean participateMatch(long uid,long mid) {
        matchMapper.addParticipateCount(mid);
        return matchMapper.addMatchUser(uid,mid);
    }

    @Override
    public List<String> getUserMatch(long uid) {
        return matchMapper.getUserMatch(uid);
    }

    /**
     * Zset实现排名
     * @param mid
     * @return
     */
    @Override
    public List<MatchResult> getMatchResult(long mid) {
        // 缓存穿透，指数据库查询为null值，在这一部分不能将数据存入
        if (!BloomFilterInitializer.mightContain(mid)) {
            // 如果布隆过滤器判断不存在，直接返回空列表，避免查询数据库
            return Collections.emptyList();
        }

        String zSetKey = "match_rank:" + mid;
        String lockKey = "lock:" + zSetKey;
        String lockValue = String.valueOf(System.currentTimeMillis());

        // 先尝试从 Redis 中获取排序后的 MatchResult 列表
        List<MatchResult> resultsFromRedis = getSortedMatchResults(zSetKey);
        if (resultsFromRedis != null && !resultsFromRedis.isEmpty()) {
            return resultsFromRedis;
        }

        // 尝试获取锁
        boolean isLocked = redisDistributedLock.tryLock(lockKey, lockValue, 10, TimeUnit.SECONDS);
        if (isLocked) {
            try {
                // 再次检查缓存，避免其他线程已经更新了缓存
                resultsFromRedis = getSortedMatchResults(zSetKey);
                if (resultsFromRedis != null && !resultsFromRedis.isEmpty()) {
                    return resultsFromRedis;
                }

                // 获取提交结果
                List<MatchResult> matchResults = recordMapper.getMatchResult(mid);
                for (MatchResult matchResult : matchResults) {
                    updateUserScore(matchResult, zSetKey);
                }

                // 设置 ZSet的过期时间，如果比赛结束那就不设置时间，永久保存即可
                // oj_match_list查询比赛信息
                String endTime = matchMapper.getMatchEndTimeWithId(mid);
                // 定义日期时间格式
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
                // 将字符串转换为 LocalDateTime 对象
                LocalDateTime endDateTime = LocalDateTime.parse(endTime, formatter);
                // 如果当前时间大于结束时间是假就设置过期时间
                if (!LocalDateTime.now().isAfter(endDateTime)) {
                    redisTemplate.expire(zSetKey, EXPIRE_TIME, TimeUnit.SECONDS);
                }
                return matchResults;
            } finally {
                // 释放锁
                redisDistributedLock.unlock(lockKey, lockValue);
            }
        } else {
            // 等待一段时间后重试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getMatchResult(mid);
        }
    }

    public void updateUserScore(MatchResult matchResult, String zSetKey) {
        try {
            String serialized = SerializationUtils.serialize(matchResult);
            // 更新ZSet中的分数
            redisTemplate.opsForZSet().add(zSetKey, serialized, matchResult.getTotalScore());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private List<MatchResult> getSortedMatchResults(String zSetKey) {
        Set<ZSetOperations.TypedTuple<String>> rankedResults = redisTemplate.opsForZSet().reverseRangeWithScores(zSetKey, 0, -1);
        List<MatchResult> results = new ArrayList<>();
        if (rankedResults != null) {
            for (ZSetOperations.TypedTuple<String> tuple : rankedResults) {
                String serialized = tuple.getValue();
                if (serialized != null) {
                    try {
                        MatchResult matchResult = SerializationUtils.deserialize(serialized);
                        results.add(matchResult);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return results;
    }

    /**
     * 删除一个竞赛
     *
     * @param mid
     * @return
     */
    @Override
    public boolean deleteMatch(long mid) {
        return matchMapper.deleteMatch(mid);
    }

    /**
     * 检查这个用户有没有参加这个竞赛
     *
     * @param uid
     * @param mid
     * @return
     */
    @Override
    public boolean checkUserParticipateMatch(long uid, long mid) {
        return matchMapper.checkUserParticipateMatch(uid, mid) >= 1;
    }

    /**
     * @param mid
     * @return
     */
    @Override
    public List<String> getMatchUserImgUrl(long mid) {
        return matchMapper.getMatchUserImgUrl(mid);
    }

    /**
     * @param match
     * @return
     */
    @Override
    @Transactional
    public boolean updateMatchInfo(Match match) {
        log.info(match.toString());
        boolean done = false;
        done = matchMapper.deleteMatchQuestion(String.valueOf(match.getId()));
        done = matchMapper.addMatchQuestion(match.getQuestionIds(), match.getId());
        done = matchMapper.updateMatch(match);
        return done;
    }


}